﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using thinger.DataConvertLib;
using thinger.ModbusRTULib;

namespace thinger.ModbusProject
{
    public partial class FrmModbusRTU : Form
    {
        public FrmModbusRTU()
        {
            InitializeComponent();

            // 初始化参数
            InitParams();
        }


        #region 属性

        /// <summary>
        /// ModbusRTU通信对象
        /// </summary>
        private ModbusRTU modbus = new ModbusRTU();

        private DataFormat dataFormat = DataFormat.ABCD;


        /// <summary>
        /// 当前连接状态
        /// </summary>
        private bool IsConnected = false;

        #endregion


        #region 初始化参数

        /// <summary>
        /// 初始化参数
        /// </summary>
        public void InitParams()
        {
            // 端口号
            string[] PortNames = SerialPort.GetPortNames();
            if (PortNames.Length > 0)
            {
                cmb_PortName.Items.AddRange(PortNames);
                cmb_PortName.SelectedIndex = 0;

            }

            // 波特率
            cmb_BaudRate.Items.AddRange(new string[] { "2400", "4800", "9600", "19200", "38400" });
            cmb_BaudRate.SelectedIndex = 2;

            // 校验位
            cmb_Parity.DataSource = Enum.GetNames(typeof(Parity));
            cmb_Parity.SelectedIndex = 0;

            // 数据位
            cmb_DataBits.Items.AddRange(new string[] { "7", "8" });
            cmb_DataBits.SelectedIndex = 1;

            // 停止位
            cmb_StopBits.DataSource = Enum.GetNames(typeof(StopBits));
            cmb_StopBits.SelectedIndex = 1;

            // 大小端(导入thinger包)
            cmb_DataFormat.DataSource = Enum.GetNames(typeof(DataFormat));
            cmb_DataFormat.SelectedIndex = 0;

            // 存储区
            cmd_StoreArea.DataSource = Enum.GetNames(typeof(StoreArea));
            cmd_StoreArea.SelectedIndex = 0;

            // 数据类型
            cmd_DataType.DataSource = Enum.GetNames(typeof(DataType));
            cmd_DataType.SelectedIndex = 0;

            // 动态修改ListView第二列的宽度
            lst_ReadInfo.Columns[1].Width = lst_ReadInfo.Width - lst_ReadInfo.Columns[0].Width - 20;
        }


        #endregion


        #region 添加日志

        /// <summary>
        /// 添加日志
        /// </summary>
        /// <param name="level">等级</param>
        /// <param name="info">信息</param>
        private void AddLog(int level, string info)
        {
            ListViewItem lst = new ListViewItem(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), level);

            lst.SubItems.Add(info);

            // 让最新的数据在最上面
            lst_ReadInfo.Items.Insert(0, lst);
        }

        #endregion


        #region 通用验证方法

        /// <summary>
        /// 通用验证方法
        /// </summary>
        /// <returns></returns>
        private bool CommonVerity()
        {
            if (IsConnected == false)
            {
                AddLog(1, "请检查是否连接正常");
                return false;
            }

            if (byte.TryParse(txt_Slaveld.Text, out _) == false)
            {
                AddLog(1, "请检查从站地址格式是否正确");
                return false;
            }

            if (ushort.TryParse(txt_Start.Text, out _) == false)
            {
                AddLog(1, "请检查起始地址格式是否正确");
                return false;
            }

            if (ushort.TryParse(txt_Length.Text, out _) == false)
            {
                AddLog(1, "请检查长度格式是否正确");
                return false;
            }

            return true;
        }

        #endregion


        #region 读取布尔值

        /// <summary>
        /// 读取布尔值
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadBool(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    result = modbus.ReadOutputCoils(slaveId, start, length);
                    break;
                case StoreArea.输入线圈1x:
                    result = modbus.ReadInputCoils(slaveId, start, length);
                    break;
                case StoreArea.输出寄存器4x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(BitLib.GetBitArrayFromByteArray(result, 0, length)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 读取Byte

        /// <summary>
        /// 读取byte
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadByte(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    result = modbus.ReadOutputCoils(slaveId, start, length);
                    break;
                case StoreArea.输入线圈1x:
                    result = modbus.ReadInputCoils(slaveId, start, length);
                    break;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(BitLib.GetBitArrayFromByteArray(result, 0, length)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 读取Short

        /// <summary>
        /// 读取short
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadShort(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(ShortLib.GetShortArrayFromByteArray(result)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 读取UShort

        /// <summary>
        /// 读取ushort
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadUShort(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(UShortLib.GetUShortArrayFromByteArray(result)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 读取Int

        /// <summary>
        /// 读取int
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadInt(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(IntLib.GetIntArrayFromByteArray(result, dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 读取UInt

        /// <summary>
        /// 读取uint
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadUInt(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(UIntLib.GetUIntArrayFromByteArray(result, dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }


        #endregion


        #region 读取Float

        /// <summary>
        /// 读取float
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void ReadFloat(StoreArea storeArea, byte slaveId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败, 不支持该存储区");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(slaveId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功: " + StringLib.GetStringFromValueArray(FloatLib.GetFloatArrayFromByteArray(result, dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }

        #endregion


        #region 写入布尔值

        /// <summary>
        /// 写入布尔值
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteBool(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    bool[] values = BitLib.GetBitArrayFromBitArrayString(setValue);

                    if (values.Length == 1)
                    {
                        result = modbus.PreSetSingleCoil(slaveId, start, values[0]);
                    }
                    else
                    {
                        result = modbus.PreSetMultiCoils(slaveId, start, values);
                    }
                    break;
                case StoreArea.输入线圈1x:
                case StoreArea.输出寄存器4x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }

        #endregion


        #region 写入Byte
        /// <summary>
        /// 写入Byte
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteByte(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    result = modbus.PreSetMultiRegisters(slaveId, start, ByteArrayLib.GetByteArrayFromHexString(setValue));
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 写入Short
        /// <summary>
        /// 写入Short
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteShort(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    short[] values = ShortLib.GetShortArrayFromString(setValue);

                    if (values.Length == 1)
                    {
                        result = modbus.PreSetSingleRegister(slaveId, start, ByteArrayLib.GetByteArrayFromHexString(setValue));
                    }

                    if (values.Length > 1)
                    {
                        result = modbus.PreSetMultiRegisters(slaveId, start, ByteArrayLib.GetByteArrayFromShortArray(values));

                    }

                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 写入UShort
        /// <summary>
        /// 写入UShort
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteUShort(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    ushort[] values = UShortLib.GetUShortArrayFromString(setValue);

                    if (values.Length == 1)
                    {
                        result = modbus.PreSetSingleRegister(slaveId, start, ByteArrayLib.GetByteArrayFromHexString(setValue));
                    }

                    if (values.Length > 1)
                    {
                        result = modbus.PreSetMultiRegisters(slaveId, start, ByteArrayLib.GetByteArrayFromUShortArray(values));

                    }

                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 写入Int
        /// <summary>
        /// 写入Int
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteInt(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    int[] values = IntLib.GetIntArrayFromString(setValue);
                    byte[] bytes = ByteArrayLib.GetByteArrayFromIntArray(values);
                    result = modbus.PreSetMultiRegisters(slaveId, start, bytes);

                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 写入UInt
        /// <summary>
        /// 写入UInt
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteUInt(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    uint[] values = UIntLib.GetUIntArrayFromString(setValue);

                    result = modbus.PreSetMultiRegisters(slaveId, start, ByteArrayLib.GetByteArrayFromUIntArray(values));

                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 写入Float
        /// <summary>
        /// 写入Float
        /// </summary>
        /// <param name="storeArea"></param>
        /// <param name="slaveId"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        private void WriteFloat(StoreArea storeArea, byte slaveId, ushort start, string setValue)
        {
            bool result = false;

            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    float[] values = FloatLib.GetFloatArrayFromString(setValue);

                    result = modbus.PreSetMultiRegisters(slaveId, start, ByteArrayLib.GetByteArrayFromFloatArray(values));

                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败, 不支持该存储区");
                    return;
            }


            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败,请检查!");
            }
        }
        #endregion


        #region 连接与断开

        /// <summary>
        /// 建立连接命令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            if (IsConnected)
            {
                AddLog(1, "Modbus已经连接");
                return;
            }

            Parity parity = (Parity)Enum.Parse(typeof(Parity), cmb_Parity.Text, true);
            StopBits stopBits = (StopBits)Enum.Parse(typeof(StopBits), cmb_StopBits.Text, true);

            IsConnected = modbus.Connect(cmb_PortName.Text, Convert.ToInt32(cmb_BaudRate.Text),
                parity, Convert.ToInt32(cmb_DataBits.Text), stopBits);

            if (IsConnected)
            {
                AddLog(0, "Modbus连接成功");
            }
            else
            {
                AddLog(2, "Modbus连接失败");
            }
        }

        /// <summary>
        /// 断开连接命令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_DisConnect_Click(object sender, EventArgs e)
        {
            if (!IsConnected)
            {
                AddLog(1, "Modbus未连接");
                return;
            }

            modbus.DisConnect();
            IsConnected = false;
            AddLog(0, "Modbus断开连接");
        }

        #endregion


        #region 读取按钮点击

        /// <summary>
        /// 读取命令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Read_Click(object sender, EventArgs e)
        {
            if (!CommonVerity())
            {
                return;
            }

            byte slaveId = byte.Parse(txt_Slaveld.Text.Trim());
            ushort start = ushort.Parse(txt_Start.Text.Trim());
            ushort length = ushort.Parse(txt_Length.Text.Trim());

            dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), cmb_DataFormat.Text, true);
            StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), cmd_StoreArea.Text, true);
            DataType dataType = (DataType)Enum.Parse(typeof(DataType), cmd_DataType.Text, true);

            switch (dataType)
            {
                case DataType.Byte:
                    ReadByte(storeArea, slaveId, start, length);
                    break;
                case DataType.Bool:
                    ReadBool(storeArea, slaveId, start, length);
                    break;
                case DataType.Short:
                    ReadShort(storeArea, slaveId, start, length);
                    break;
                case DataType.UShort:
                    ReadUShort(storeArea, slaveId, start, length);
                    break;
                case DataType.Int:
                    ReadInt(storeArea, slaveId, start, length);
                    break;
                case DataType.UInt:
                    ReadUInt(storeArea, slaveId, start, length);
                    break;
                case DataType.Float:
                    ReadFloat(storeArea, slaveId, start, length);
                    break;
                case DataType.Double:
                case DataType.Long:
                case DataType.ULong:
                case DataType.String:
                case DataType.ByteArray:
                case DataType.HexString:
                    AddLog(1, "读取失败，暂时不支持该类型");
                    return;
            }
        }

        #endregion


        #region 写入按钮点击

        /// <summary>
        /// 写入按钮点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Write_Click(object sender, EventArgs e)
        {
            if (!CommonVerity())
            {
                return;
            }

            byte slaveId = byte.Parse(txt_Slaveld.Text.Trim());
            ushort start = ushort.Parse(txt_Start.Text.Trim());
            ushort length = ushort.Parse(txt_Length.Text.Trim());

            dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), cmb_DataFormat.Text, true);
            StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), cmd_StoreArea.Text, true);
            DataType dataType = (DataType)Enum.Parse(typeof(DataType), cmd_DataType.Text, true);
            string writeValue = txt_WriteValue.Text.Trim();

            switch (dataType)
            {
                case DataType.Bool:
                    WriteBool(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.Byte:
                    WriteByte(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.Short:
                    WriteShort(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.UShort:
                    WriteUShort(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.Int:
                    WriteInt(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.UInt:
                    WriteUInt(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.Float:
                    WriteFloat(storeArea, slaveId, start, writeValue);
                    break;
                case DataType.Double:
                case DataType.Long:
                case DataType.ULong:
                case DataType.String:
                case DataType.ByteArray:
                case DataType.HexString:
                    AddLog(1, "写入失败，暂时不支持该类型");
                    return;
            }
        }

        #endregion
    }
}
